﻿namespace NetFx.MSIL
{
  using System;
  using System.IO;
  using System.Text;
  using System.Threading;
  using System.Collections;

  using System.Reflection;
  using System.Reflection.Emit;

  using System.Resources;
  using System.Globalization;
  using System.Security;
  using System.Security.Policy;
  using System.Security.Permissions;

  public class AssemblyGenerator:AnyGenerator
  {
    #region Statics
    public static AppDomain CurrentDomain
    {
      get
      {
        return Thread.GetDomain();
      }
    }
    public static AssemblyGenerator of(AssemblyBuilder builder)
    {
      return new AssemblyGenerator(builder);
    }
    public static AssemblyGenerator DefineAssembly(string assemblyName, AssemblyBuilderAccess access)
    {
      return DefineAssembly(new AssemblyName(assemblyName), access);
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName assemblyName, AssemblyBuilderAccess access)
    {
      return of(CurrentDomain.DefineDynamicAssembly(assemblyName, access));
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName name, AssemblyBuilderAccess access, Evidence evidence)
    {
      return of(CurrentDomain.DefineDynamicAssembly(name, access, evidence));
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName name, AssemblyBuilderAccess access, string dir)
    {
      return of(CurrentDomain.DefineDynamicAssembly(name, access, dir));
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName name, AssemblyBuilderAccess access, string dir, Evidence evidence)
    {
      return of(CurrentDomain.DefineDynamicAssembly(name, access, dir, evidence));
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName name, AssemblyBuilderAccess access, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions)
    {
      return of(CurrentDomain.DefineDynamicAssembly(name, access, requiredPermissions, optionalPermissions, refusedPermissions));
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName name, AssemblyBuilderAccess access, string dir, Evidence evidence, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions)
    {
      return of(CurrentDomain.DefineDynamicAssembly(name, access, dir, requiredPermissions, optionalPermissions, refusedPermissions));
    }
    public static AssemblyGenerator DefineAssembly(AssemblyName name, AssemblyBuilderAccess access, string dir, Evidence evidence, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions, bool isSynchronized)
    {
      return of(CurrentDomain.DefineDynamicAssembly(name, access, dir,evidence, requiredPermissions, optionalPermissions, refusedPermissions,isSynchronized));
    }
    #endregion
    #region Fields
    protected AssemblyBuilder builder;
    protected ArrayList moduleGenerators = new ArrayList();
    #endregion
    #region Properties
    public override object AnyBuilder
    {
      get { return this.builder; }
    }
    public override DefType GeneratorType
    {
      get
      {
        return DefType.Assembly;
      }
    }
    public virtual string FullName
    {
      get
      {
        return this.Builder.FullName;
      }
    }
    public virtual AssemblyName Name
    {
      get
      {
        return this.Builder.GetName();
      }
    }
    public virtual AssemblyName[] ReferencedAssemblies
    {
      get
      {
        return this.Builder.GetReferencedAssemblies();
      }
    }
    public virtual AssemblyBuilder Builder
    {
      get
      {
        return this.builder;
      }
    }
    public virtual MethodInfo EntryPoint
    {
      get
      {
        return this.Builder.EntryPoint;
      }
    }
    public virtual IList ModuleGenerators
    {
      get
      {
        return this.moduleGenerators;
      }
    }
    public virtual string ImageRuntimeVersion
    {
      get
      {
        return this.Builder.ImageRuntimeVersion;
      }
    }
    public virtual Type[] ExportedTypes
    {
      get
      {
        return this.Builder.GetExportedTypes();
      }
    }
    #endregion
    #region Constructors
    public AssemblyGenerator(AssemblyBuilder builder)
    {
      if (builder == null) throw new ArgumentNullException("builder can not be null!");
      this.builder = builder;
    }
    #endregion
    #region Methods
    #region Define Modules
    public virtual ModuleGenerator DefineModule(string name)
    {
      return this.SetModuleGenerator(
        name,
        this.Builder.DefineDynamicModule(name)
        );
    }
    public virtual ModuleGenerator DefineModule(string name, bool emitSymbolInfo)
    {
      return this.SetModuleGenerator(
        name,
        this.Builder.DefineDynamicModule(name, emitSymbolInfo)
        );
    }
    public virtual ModuleGenerator DefineModule(string name, string filename)
    {
      return this.SetModuleGenerator(
        name,
        this.Builder.DefineDynamicModule(name, filename)
        );
    }
    public virtual ModuleGenerator DefineModule(string name, string filename, bool emitSymbolInfo)
    {
      return this.SetModuleGenerator(
        name,
        this.Builder.DefineDynamicModule(name,filename,emitSymbolInfo)
        );
    }
    #region Get Module
    public virtual ModuleGenerator GetModuleGenerator(string name)
    {
      ModuleGenerator generator = this.GetGenerator(name);
      if (generator == null)
      {
        generator =this.SetModuleGenerator(name, this.Builder.GetDynamicModule(name));
      }
      return generator;
    }
    public virtual ModuleGenerator SetModuleGenerator(string name, ModuleBuilder builder)
    {
      ModuleGenerator generator = this.GetGenerator(name);

      if (generator != null)
      {
        generator.Builder = builder;
      }
      else
      {
        generator = this.AddGenerator(new ModuleGenerator(builder));

      }
      return generator;
    }
    protected virtual ModuleGenerator AddGenerator(ModuleGenerator g)
    {
      this.moduleGenerators.Add(g);
      return g;
    }
    protected virtual ModuleGenerator GetGenerator(string name)
    {
      foreach (ModuleGenerator g in this.moduleGenerators)
      {
        if (g.Builder.Name == name)
          return g;
      }
      return null;
    }
    #endregion
    #endregion
    #region Resource Files
    public virtual void AddResourceFile(
      string name,
      string fileName
      )
    {
      this.Builder.AddResourceFile(name, fileName);
    }
    public virtual void AddResourceFile(string name, string fileName, ResourceAttributes attribute)
    {
      this.Builder.AddResourceFile(name, fileName, attribute);
    }

    public virtual IResourceWriter DefineResource(
      string name,
      string description,
      string fileName
      )
    {
      return this.Builder.DefineResource(name, description, fileName);
    }
    public virtual IResourceWriter DefineResource(
       string name,
       string description,
       string fileName,
       ResourceAttributes attribute
      )
    {
      return this.Builder.DefineResource(
        name,
        description,
        fileName,
        attribute
        );
    }
    public virtual void DefineUnmanagedResource(
       byte[] resource
      )
    {
      this.Builder.DefineUnmanagedResource(resource);
    }
    public virtual void DefineUnmanagedResource(
       string resourceFileName
    )
    {
      this.Builder.DefineUnmanagedResource(resourceFileName);
    }

    public virtual void DefineVersionInfoResource()
    {
      this.Builder.DefineVersionInfoResource();
    }
    public virtual void DefineVersionInfoResource(
      string product,
      string productVersion,
      string company,
      string copyright,
      string trademark
    )
    {
      this.Builder.DefineVersionInfoResource(product, productVersion, company, copyright, trademark);
    }

    public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName)
    {
      return this.Builder.GetManifestResourceInfo(resourceName);
    }
    public virtual string[] GetManifestResourceNames()
    {
      return this.Builder.GetManifestResourceNames();
    }
    public virtual Stream GetManifestResourceStream(string name)
    {
      return this.Builder.GetManifestResourceStream(name);
    }
    public virtual Stream GetManifestResourceStream(Type type, string name)
    {
      return this.Builder.GetManifestResourceStream(type, name);
    }
    #endregion
    #region CreateInstance
    public virtual object CreateInstance(
       string typeName
    )
    {
      return this.Builder.CreateInstance(typeName);
    }
    public virtual object CreateInstance(string typeName, bool ignoreCase)
    {
      return this.Builder.CreateInstance(typeName, ignoreCase);
    }
    public virtual object CreateInstance(
      string typeName,
      bool ignoreCase,
      BindingFlags bindingAttr,
      Binder binder,
      object[] args,
      CultureInfo culture,
      object[] activationAttributes
    )
    {
      return this.Builder.CreateInstance(
        typeName,
        ignoreCase,
        bindingAttr,
        binder,
        args,
        culture,
        activationAttributes
        );
    }
    #endregion
    #region EntryPoint
    public virtual void SetEntryPoint(MethodInfo entryMethod)
    {
      this.Builder.SetEntryPoint(entryMethod);
    }
    public virtual void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind)
    {
      this.Builder.SetEntryPoint(entryMethod, fileKind);
    }
    #endregion
    #region File
    public virtual FileStream GetFile(string name)
    {
      return this.Builder.GetFile(name);
    }
    public virtual FileStream[] GetFiles()
    {
      return this.Builder.GetFiles();
    }
    public virtual FileStream[] GetFiles(bool includingRes)
    {
      return this.Builder.GetFiles(includingRes);
    }

    public virtual void SaveAssembly(string assemblyFileName)
    {
      this.Builder.Save(assemblyFileName);
    }

    #endregion
    #region Utils
    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.builder = null;
      }
      base.Dispose(disposing);
    }
    protected override void DisposeCollection(bool disposing)
    {
      if (disposing)
      {
        this.DisposeCollection(this.moduleGenerators);
      }
      base.DisposeCollection(disposing);
    }
    #endregion
    #endregion
  }
}
